package com.ctrip.ec.api.mobile.controller;


import com.ctrip.ec.api.mobile.aop.AuthLogin;
import com.ctrip.ec.api.mobile.common.RequestContext;
import com.ctrip.ec.api.mobile.common.ValidateException;
import com.ctrip.ec.api.mobile.config.WxPayProperties;
import com.ctrip.ec.api.mobile.model.payment.CartModel;
import com.ctrip.ec.api.mobile.model.payment.CreateOrderResponse;
import com.ctrip.ec.api.mobile.model.payment.PaymentWechatModel;

import com.ctrip.ec.biz.dao.agent.AgentDao;
import com.ctrip.ec.biz.dao.cart.CartDao;
import com.ctrip.ec.biz.dao.goods.GoodsDao;
import com.ctrip.ec.biz.dao.goods.GoodsRebateDao;
import com.ctrip.ec.biz.dao.goods.GoodsSpecDao;
import com.ctrip.ec.biz.dao.member.MemberAddressDao;
import com.ctrip.ec.biz.dao.member.MemberDao;
import com.ctrip.ec.biz.dao.member.MemberOauthDao;
import com.ctrip.ec.biz.dao.order.OrderDao;
import com.ctrip.ec.biz.dao.order.OrderGoodsDao;
import com.ctrip.ec.biz.dao.payment.PaymentDao;
import com.ctrip.ec.biz.dao.rebate.RebateHistoryDao;
import com.ctrip.ec.biz.dao.sequence.SequenceDao;
import com.ctrip.ec.biz.entity.agent.AgentEntity;
import com.ctrip.ec.biz.entity.cart.CartEntity;
import com.ctrip.ec.biz.entity.goods.GoodsEntity;
import com.ctrip.ec.biz.entity.goods.GoodsSpecEntity;
import com.ctrip.ec.biz.entity.member.MemberAddressEntity;
import com.ctrip.ec.biz.entity.member.MemberEntity;
import com.ctrip.ec.biz.entity.member.OAuthConstants;
import com.ctrip.ec.biz.entity.order.OrderEntity;
import com.ctrip.ec.biz.entity.order.OrderGoodsEntity;
import com.ctrip.ec.biz.entity.order.OrderGoodsStatus;
import com.ctrip.ec.biz.entity.order.OrderStatus;
import com.ctrip.ec.biz.entity.payment.PaymentEntity;
import com.ctrip.ec.biz.entity.payment.PaymentStatus;
import com.ctrip.ec.biz.entity.sequence.IdResult;
import com.ctrip.ec.biz.util.RequestUtil;
import com.ctrip.ec.dal.IdConvert;
import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

@RestController
@RequestMapping("payment")
public class PaymentController {
    @Autowired
    private PaymentDao payment;

    @Autowired
    private GoodsSpecDao goodsSpec;

    @Autowired
    private OrderDao order;

    @Autowired
    private OrderGoodsDao orderGoods;

    @Autowired
    private MemberDao member;

    @Autowired
    private RebateHistoryDao rebateHistory;

    @Autowired
    private MemberAddressDao memberAddress;

    @Autowired
    private CartDao cart;

    @Autowired
    private GoodsDao goods;

    @Autowired
    private GoodsRebateDao goodsRebate;

    @Autowired
    private SequenceDao sequence;

    @Autowired
    private WxPayService wxService;

    @Autowired
    private WxPayProperties wxProperties;

    @Autowired
    private MemberOauthDao memberOauth;


    @Autowired
    private AgentDao agent;


    @Value("${miniapp.notifyUrl}")
    private  String miniappNotifyUrl;

    private final static Logger logger = LoggerFactory.getLogger(PaymentController.class);

    private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");


    private static final SimpleDateFormat yyyyMMdd = new SimpleDateFormat("yyyyMMdd");

    private static final Random rnd = new Random();

    @AuthLogin
    @GetMapping("detail")
    public PaymentEntity detail(@RequestParam Long id) throws SQLException {
        PaymentEntity paymentEntity = payment.entity(id);
        if (!paymentEntity.getMemberId().equals(RequestContext.getMemberId())) {
            throw new ValidateException("非法查看订单");
        }
        return payment.entity(id);
    }

    private String genSn() throws SQLException {
        IdResult idResult = sequence.maxIdByDay("order_sn");
        Timestamp now = idResult.getNow();
        Long maxId = idResult.getMaxId();
        int r = rnd.nextInt(100); //加两位随机数,让别人不可猜
        //订单号共16位
        String sn = yyyyMMdd.format(now) + String.format("%06d", maxId) + String.format("%02d", r);//一天最多100万订单

        return sn;
    }

    @AuthLogin
    @PostMapping("createOrder")
    public CreateOrderResponse createOrder(@RequestBody PaymentWechatModel request) throws Exception {

        if (request.getMemberAddressId() == null || request.getMemberAddressId() == 0) {
            throw new ValidateException("请选择收货地址");
        }
        if (request.getCarts() == null || request.getCarts().size() == 0) {
            throw new ValidateException("请选择购物车里的商品");
        }
//        if(request.getGoodsAmount().compareTo(new BigDecimal(0))<1){
//            throw new ValidateException("订单金额必须大于0");
//        }

        Map<String, BigDecimal> amount = confirm(request.getCarts());
        BigDecimal goodsAmount = amount.get("goodsAmount");
        BigDecimal shippingAmount = amount.get("shippingAmount");
//        if(!goodsAmount.equals(request.getGoodsAmount())){
//            throw new ValidateException("总金额不对，请检查");
//        }

        //启用事务写订单,同时扣库存
        try {
            payment.getDb().begin();
            PaymentEntity paymentEntity = new PaymentEntity();
            paymentEntity.setMemberId(RequestContext.getMemberId());
            paymentEntity.setCouponAmount(new BigDecimal(0));
            paymentEntity.setOrderTime(new Timestamp(System.currentTimeMillis()));
            paymentEntity.setName("");
            paymentEntity.setStatus(PaymentStatus.未支付.ordinal());
            paymentEntity.setGoodsAmount(goodsAmount);
            paymentEntity.setPaymentAmount(goodsAmount.add(shippingAmount));
            paymentEntity.setSn(genSn());
            paymentEntity.setAgentId(RequestContext.getAgentId());
            payment.save(paymentEntity);

            //取出送货地址
            MemberAddressEntity addressEntity = memberAddress.entity(request.getMemberAddressId());

            //检查库存
            List<GoodsSpecEntity> specList = checkStore(request.getCarts());

            List<GoodsEntity> goodsList = goods.listByIds(specList.stream().map(t -> t.getGoodsId()).collect(Collectors.toList()));

            MemberEntity memberEntity = member.entityWithLock(RequestContext.getMemberId());



            for (CartModel cartModel : request.getCarts()) {
                CartEntity cartEntity = cart.entity(cartModel.getId());
                cartModel.setUserNote(cartEntity.getUserNote());
                if (cartEntity == null) {
                    throw new ValidateException("购物车数据已经被清空，请重新选择");
                }

                Optional<GoodsSpecEntity> first = specList.stream().filter(t -> t.getId().equals(cartEntity.getSpecId())).findFirst();
                GoodsSpecEntity specEntity = first.get();

                //减库存
                specEntity.setStoreCount(specEntity.getStoreCount() - cartModel.getGoodsNum());
                goodsSpec.save(specEntity);

                //删除购物车
                cart.delete(cartEntity.getId());

            }

            //保存订单,可能拆单

            Map<Long, List<GoodsEntity>> goodsGroup = goodsList.stream().collect(Collectors.groupingBy(t -> t.getStoreId()));

            List<Long> orderIds = new ArrayList<>();
            for (Map.Entry<Long, List<GoodsEntity>> entry : goodsGroup.entrySet()) {
                OrderEntity orderEntity = new OrderEntity();
                orderEntity.setPaymentId(paymentEntity.getId());
                orderEntity.setStoreId(entry.getKey());
                orderEntity.setMemberId(RequestContext.getMemberId());
                orderEntity.setAddress(addressEntity.getProvince() + " " + addressEntity.getCity() + " " + addressEntity.getDistrict() + " " + addressEntity.getAddress());
                orderEntity.setAreaId(addressEntity.getAreaId());
                orderEntity.setConsignee(addressEntity.getConsignee());
                orderEntity.setEmail(addressEntity.getEmail());
                orderEntity.setIsDeleted(false);
                orderEntity.setMemberName(memberEntity.getNickname());
                orderEntity.setMobile(addressEntity.getMobile());
                orderEntity.setOrderStatus(OrderStatus.待支付.getId());
                orderEntity.setOrderTime(new Timestamp(System.currentTimeMillis()));
                orderEntity.setZipcode(addressEntity.getZipcode());
                orderEntity.setCouponAmount(new BigDecimal(0));
                orderEntity.setShippingAmount(new BigDecimal(0));//运费暂为0
                orderEntity.setGoodsAmount(new BigDecimal(0));//商品总价
                orderEntity.setAgencyAmount(new BigDecimal(0));//代理费总价
                orderEntity.setAgentId(RequestContext.getAgentId());
                orderEntity.setSn(genSn());

                order.save(orderEntity);//先保存生成id
                orderIds.add(orderEntity.getId());


                for (GoodsEntity goodsEntity : entry.getValue()) {
                    //找到购买的所有规格，一个人同一个商品可能购买多个规格
                    List<GoodsSpecEntity> goodsSpecList = specList.stream().filter(t -> t.getGoodsId().equals(goodsEntity.getId())).collect(Collectors.toList());
                    for (GoodsSpecEntity goodsSpecEntity : goodsSpecList) {

                        CartModel cartModel = request.getCarts().stream().filter(t -> t.getSpecId().equals(goodsSpecEntity.getId())).findFirst().get();
                        orderEntity.setGoodsAmount(orderEntity.getGoodsAmount().add(goodsSpecEntity.getShopPrice().multiply(new BigDecimal(cartModel.getGoodsNum()))));
                        orderEntity.setAgencyAmount(orderEntity.getAgencyAmount().add(goodsSpecEntity.getAgencyPrice().multiply(new BigDecimal(cartModel.getGoodsNum()))));

                        //保存订单商品表
                        OrderGoodsEntity orderGoodsEntity = new OrderGoodsEntity();
                        orderGoodsEntity.setGoodsId(goodsEntity.getId());
                        orderGoodsEntity.setGoodsSn(goodsEntity.getSn());
                        orderGoodsEntity.setUserNote(cartModel.getUserNote());
                        orderGoodsEntity.setAgencyPrice(goodsSpecEntity.getAgencyPrice());
                        orderGoodsEntity.setBuyNum(cartModel.getGoodsNum());
                        orderGoodsEntity.setCreateTime(new Timestamp(System.currentTimeMillis()));
                        orderGoodsEntity.setCostPrice(goodsSpecEntity.getCostPrice());
                        orderGoodsEntity.setFinalPrice(goodsSpecEntity.getShopPrice());//目前没有折扣
                        orderGoodsEntity.setShopPrice(goodsSpecEntity.getShopPrice());
                        orderGoodsEntity.setMarketPrice(goodsSpecEntity.getMarketPrice());
                        orderGoodsEntity.setGoodsName(goodsEntity.getName());
                        orderGoodsEntity.setGoodsSn(goodsEntity.getSn());
                        orderGoodsEntity.setGoodsImage(goodsEntity.getOriginalImg());
                        orderGoodsEntity.setIsClose(false);
                        orderGoodsEntity.setIsComment(false);
                        orderGoodsEntity.setOrderId(orderEntity.getId());
                        orderGoodsEntity.setPaymentId(paymentEntity.getId());
                        orderGoodsEntity.setSpecId(goodsSpecEntity.getId());
                        orderGoodsEntity.setSpecName(goodsSpecEntity.getName());
                        orderGoodsEntity.setSpecTitle(goodsEntity.getSpecTitle());
                        orderGoodsEntity.setRebateTypeId(goodsEntity.getRebateTypeId());
                        orderGoodsEntity.setStoreId(goodsEntity.getStoreId());
                        orderGoodsEntity.setStatus(OrderGoodsStatus.未发货.ordinal());
                        orderGoods.save(orderGoodsEntity);
                    }
                }
                orderEntity.setPaymentAmount(orderEntity.getGoodsAmount().add(orderEntity.getShippingAmount()));//支付金额等于商品价格加运费
                order.save(orderEntity);
            }
            //把订单id全部存进去，订单可能会生成新的payment
            paymentEntity.setRemark(IdConvert.join(orderIds));
            payment.save(paymentEntity);
            payment.getDb().commit();
            CreateOrderResponse response = new CreateOrderResponse();
            response.setPaymentId(paymentEntity.getId());
            response.setPaymentSn(paymentEntity.getSn());
            response.setOrderIds(orderIds);
            return response;
        } catch (Exception ex) {
            payment.getDb().rollback();
            throw ex;
        }
    }

    @AuthLogin
    @PostMapping("createPayment")
    public CreateOrderResponse createPayment(@RequestBody List<Long> orderIds) throws Exception {
        if (orderIds == null || orderIds.size() == 0) {
            throw new ValidateException("缺少订单");
        }
        //对订单做验证
        List<OrderEntity> list = order.listByIds(orderIds);


        //List<PaymentEntity> paymentList = payment.listByIds(list.stream().map(t->t.getPaymentId()).distinct().collect(Collectors.toList()));

        try {
            payment.getDb().begin();

            BigDecimal goodsAmount = new BigDecimal(0);
            BigDecimal shippingAmount = new BigDecimal(0);

            for (OrderEntity entity : list) {
                if (!entity.getMemberId().equals(RequestContext.getMemberId())) {
                    throw new ValidateException("非法定单");
                }
                goodsAmount = goodsAmount.add(entity.getGoodsAmount());
                shippingAmount = shippingAmount.add(entity.getShippingAmount());
            }

            //先取消支付订单的状态
            payment.cancel(list.stream().map(t -> t.getPaymentId()).distinct().collect(Collectors.toList()));

            PaymentEntity paymentEntity = new PaymentEntity();
            paymentEntity.setMemberId(RequestContext.getMemberId());
            paymentEntity.setCouponAmount(new BigDecimal(0));
            paymentEntity.setOrderTime(new Timestamp(System.currentTimeMillis()));
            paymentEntity.setName("");
            paymentEntity.setStatus(PaymentStatus.未支付.ordinal());
            paymentEntity.setGoodsAmount(goodsAmount);
            paymentEntity.setPaymentAmount(goodsAmount.add(shippingAmount));
            paymentEntity.setSn(genSn());
            paymentEntity.setAgentId(RequestContext.getAgentId());
            payment.save(paymentEntity);

            //修改订单的paymentId
            for (OrderEntity entity : list) {
                entity.setPaymentId(paymentEntity.getId());
                order.save(entity);
            }

            payment.getDb().commit();
            CreateOrderResponse response = new CreateOrderResponse();
            response.setPaymentId(paymentEntity.getId());
            response.setPaymentSn(paymentEntity.getSn());
            response.setOrderIds(orderIds);
            return response;

        } catch (Exception ex) {
            payment.getDb().rollback();
            throw ex;
        }
    }


    /**
     * 小程序专用
     *
     * @param map
     * @param request
     * @return
     * @throws SQLException
     * @throws WxPayException
     */
    @PostMapping("prepay")
    @AuthLogin
    public WxPayMpOrderResult prepay(@RequestBody Map<String, Object> map, HttpServletRequest request) throws SQLException, WxPayException {

        Long paymentId = Long.parseLong(map.get("paymentId").toString());
        PaymentEntity paymentEntity = payment.entity(paymentId);
        paymentEntity.setName("小程序支付");
        String openId = memberOauth.entity(RequestContext.getMemberId(), OAuthConstants.MINIAPP).getOpenId();
        String clientIp = RequestUtil.getClientIp(request);
        if (clientIp.equals("0:0:0:0:0:0:0:1")) {
            clientIp = "127.0.0.1";
        }

        WxPayUnifiedOrderRequest wxRequest = WxPayUnifiedOrderRequest.newBuilder()
                .body("shop")
                .totalFee(paymentEntity.getPaymentAmount().multiply(new BigDecimal(100)).intValue())
                .spbillCreateIp(clientIp)
                .notifyUrl(miniappNotifyUrl)
                .tradeType(WxPayConstants.TradeType.JSAPI)
                .openid(openId)
                .outTradeNo(paymentEntity.getSn())
                .build();
        wxRequest.setSignType(WxPayConstants.SignType.MD5);
        //wxRequest.setTradeType("JSAPI");

        WxPayService service = createByAgentId(paymentEntity.getAgentId());
        WxPayMpOrderResult returnResult = service.createOrder(wxRequest);


        String prepayId =  returnResult.getPackageValue().split("=")[1];
        paymentEntity.setPrepayId(prepayId);
        payment.save(paymentEntity);//保存prepayid

        return returnResult;
    }

    private WxPayService createByAgentId(Long agentId) throws SQLException {
        AgentEntity agentEntity = agent.entity(agentId);

        WxPayConfig payConfig = new WxPayConfig();
        payConfig.setAppId(StringUtils.trimToNull(agentEntity.getAppId()));
        payConfig.setMchId(StringUtils.trimToNull(agentEntity.getMchId()));
        payConfig.setMchKey(StringUtils.trimToNull(agentEntity.getMchKey()));
//        payConfig.setSubAppId(StringUtils.trimToNull(this.properties.getSubAppId()));
//        payConfig.setSubMchId(StringUtils.trimToNull(this.properties.getSubMchId()));
//        payConfig.setKeyPath(StringUtils.trimToNull(this.properties.getKeyPath()));

        // 可以指定是否使用沙箱环境
        payConfig.setUseSandboxEnv(false);

        WxPayService wxPayService = new WxPayServiceImpl();
        wxPayService.setConfig(payConfig);
        return wxPayService;
    }


    private List<GoodsSpecEntity> checkStore(List<CartModel> cartList) throws SQLException {
        List<Long> specIds = cartList.stream().map(t -> t.getSpecId()).collect(Collectors.toList());

        //带锁检查
        List<GoodsSpecEntity> list = goodsSpec.listWithLock(specIds);
        for (CartModel m : cartList) {
            GoodsSpecEntity specEntity = list.stream().filter(t -> t.getId().equals(m.getSpecId())).findFirst().get();

            GoodsEntity goodsEntity = goods.entity(specEntity.getGoodsId());

            //检查是否下架
            if (goodsEntity.getIsOnSale() == false) {
                throw new ValidateException("商品" + goodsEntity.getName() + "已下架");
            }

            if (m.getGoodsNum() > specEntity.getStoreCount()) {

                throw new ValidateException("商品" + goodsEntity.getName() + "规格" + specEntity.getName() + "库存不足，请重新选择");
            }


        }

        return list;

    }

    @AuthLogin
    @PostMapping("confirm")
    public Map<String, BigDecimal> confirm(@RequestBody List<CartModel> cartList) throws SQLException {
        if (cartList == null || cartList.size() == 0) {
            return null;
        }
        //对库存进行检查
        checkStore(cartList);

        BigDecimal totalAmount = new BigDecimal(0);

        for (CartModel cartModel : cartList) {
            CartEntity cartEntity = cart.entity(cartModel.getId());
            if (cartEntity == null) {
                throw new ValidateException("购物车数据已经被清空，请重新选择");
            }
            GoodsSpecEntity specEntity = goodsSpec.entity(cartEntity.getSpecId());
            totalAmount = totalAmount.add(specEntity.getShopPrice().multiply(new BigDecimal(cartModel.getGoodsNum())));
        }

        Map<String, BigDecimal> result = new HashMap<>();
        result.put("goodsAmount", totalAmount);
        result.put("shippingAmount", new BigDecimal(0));
        return result;

    }


    @GetMapping("test")
    public String test() throws WxPayException {
        logger.info("hello");
        logger.info(System.getProperty("user.dir"));

        return System.getProperty("user.dir");
    }

//    @PostMapping("/unifiedOrder")
//      public WxPayUnifiedOrderResult unifiedOrder(@RequestBody WxPayUnifiedOrderRequest request) throws WxPayException {
//        return this.wxService.unifiedOrder(request);
//    }
//
}
